﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Diagnostics;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using iTextSharp.text.pdf;
using iTextSharp.text;
using System.Drawing.Printing;
using Made4Print;

/*
 * A set of functions for converting between various file formats.  Depends on having any
 * (preferrably 8.0 or above) version of GPL Ghostscript and hpgs installed.
 * Written 18 June 2008 by Steve Lickenbrock and Paul Schorfheide
 */

namespace KorteCoConvert
{
    class Conversions
    {
        //Location of gswin32.exe.  This is initialized by gs_Initialize().
        public static string gsPath;
        //Processes for conversions.
        private static Process gs = new Process();
        private static Process hpgs = new Process();
        //Boolean variables to determine if a process has been initialized.
        private static bool gsInitialized = false;
        private static bool hpgsInitialized = false;

        /// <summary>
        /// Returns the name of the input file appended to the output directory without an extension.
        /// </summary>
        /// <param name="fullName">Fully qualified name of the input file.</param>
        /// <param name="outputDir">Directory for output files.</param>
        /// <returns>Fully qualified name of the output file.</returns>
        private static string parseOutput(string fullName, string outputDir)
        {
            //finds the last \, and removes everything before it.
            for (int num = fullName.Length - 1; num >= 0; num--)
            {
                if (fullName[num] == '\\')
                {
                    fullName = fullName.Substring(num + 1);
                    break;
                }
            }
            //Removes the filetype extension.
            fullName = fullName.Substring(0, fullName.IndexOf('.'));
            //Appends a \ to the output directory, if necessary.
            if (outputDir[outputDir.Length - 1] != '\\')
                outputDir += "\\";
            outputDir += fullName;
            return outputDir;
        }
        
        /// <summary>
        /// Uses GhostScript to convert an input file to a tiff file.
        /// </summary>
        /// <param name="input">The fullly qualified name of the input file.</param>
        /// <param name="output">The output directory.</param>
        public static void tiff(string input, string output)
        {
            string outFile = parseOutput(input, output) + ".tif";
            //Invokes gs_Run for pdf input.
            if (fileType(input) == "pdf")
            {               
                gs_Run("-dNOPAUSE -dBATCH -sOutputFile=\"" + outFile + "\" -sDEVICE=tiffg4 \"" + input + "\"");
            }
            else if (fileType(input) == "plt")
            {
                //Invokes hpgs_Run to create an EPS file.
                hpgs_Run("-i -o C:\\temp.eps \"" + input + "\"");
                //Converts the resulting file with Ghostscript
                gs_Run("-dNOPAUSE -dBATCH -dEPSCrop -sOutputFile=\"" + outFile + "\" -sDEVICE=tiffg4 C:\\temp.eps");
            }
            //If the input file did not have an extension, throws an exception.
            else if (fileType(input) == null)
                throw new ArgumentException("Not a valid file name");
            //If the input type has not been coded in yet, throws an exception.
            else
                throw new ArgumentException("Filetype \"" + fileType(input) + "\" is not supported.");
        }
        
        /// <summary>
        /// Uses GhostScript to convert an input file to a jpeg file.
        /// </summary>
        /// <param name="input">The fullly qualified name of the input file.</param>
        /// <param name="output">The output directory.</param>
        public static void jpeg(string input, string output)
        {
            string outFile = parseOutput(input, output) + ".jpg";
            if (fileType(input) == "pdf")
            {
                FileStream outStream = new FileStream(outFile, FileMode.Create);
                PdfReader read = new PdfReader(input);
                byte[] data = read.GetPageContent(1);
                MemoryStream dataStream = new MemoryStream(data);
                System.Drawing.Image rhombus = System.Drawing.Image.FromStream(dataStream);
                rhombus.Save(outStream, ImageFormat.Jpeg);
                //gs_Run("-dNOPAUSE -dBATCH -sOutputFile=\"" + outFile + "\" -sDEVICE=jpeg \"" + input + "\"");
                dataStream.Close();
            }
            else if (fileType(input) == "plt")
            {
                hpgs_Run("-i -o C:\\temp.eps \"" + input + "\"");
                gs_Run("-dNOPAUSE -dBATCH -dEPSCrop -sOutputFile=\"" + outFile + "\" -sDEVICE=jpeg C:\\temp.eps");
            }
            else if (fileType(input) == null)
                throw new ArgumentException("Not a valid file name");
            else
                throw new ArgumentException("Filetype \"" + fileType(input) + "\" is not supported.");
        }

        /// <summary>
        /// Converts an input file into a pdf file.  Throws an ArgumentException for unsupported filetypes.
        /// </summary>
        /// <param name="input">Fully-qualified name of input file.</param>
        /// <param name="output">Fully-qualified output directory.</param>
        public static void pdf(string input, string output)
        {
            string outFile = parseOutput(input, output) + ".pdf";
            if (fileType(input) == "plt")
            {
                hpgs_Run("-i -o C:\\temp.eps \"" + input + "\"");
                gs_Run("-dNOPAUSE -dBATCH -dEPSCrop -sOutputFile=\"" + outFile + "\" -sDEVICE=pdfwrite C:\\temp.eps");
            }
            else if (fileType(input) == "tif")
            {
                System.Drawing.Image multiTiff = System.Drawing.Image.FromFile(input);
                int pages = multiTiff.GetFrameCount(FrameDimension.Page);
                Document pdfOut = new Document();
                PdfWriter.GetInstance(pdfOut, new FileStream(outFile, FileMode.Create));
                pdfOut.Open();
                for (int i = 0; i < pages; i++)
                {
                    iTextSharp.text.Image singleTiff = iTextSharp.text.Image.GetInstance(getTiffImage(multiTiff, i), ImageFormat.Tiff);
                    singleTiff.ScaleToFit(612, 792);
                    pdfOut.Add(singleTiff);
                }
                pdfOut.Close();
            }
            else if (fileType(input) == null)
                throw new ArgumentException("Not a valid file name");
            else
                throw new ArgumentException("Filetype \"" + fileType(input) + "\" is not supported.");
        }

        /// <summary>
        /// Prints an input file on the default printer using Ghostscript
        /// </summary>
        /// <param name="input">Fully-qualified name of input file.</param>
        public static void print(string input)
        {
            if (fileType(input) == "ps" || fileType(input) == "pdf")
            {
                //If input is a PS or PDF file, ghostscript can handle printing them directly.
                gs_Run("-dBATCH -dNOPAUSE -sOutputFile=\"C:\\temp.png\" -sDEVICE=png16m \"" + input + "\"");
                printPng();
            }
            else if (fileType(input) == "plt")
            {
                hpgs_Run("-i -o C:\\temp.png -d png_rgb \"" + input + "\"");
                printPng();
            }
            else if (fileType(input) == null)
                throw new ArgumentException("Not a valid file name.");
            else
                throw new ArgumentException("Filetype \"" + fileType(input) + "\" is not supported.");
        }


        private static void printDoc_PrintPage(object sender, PrintPageEventArgs e)
        {
            //Set up a new rectange spanning the page.
            RectangleF rect = new RectangleF(0, 0, e.PageBounds.Width, e.PageBounds.Height);
            //Load the image to be printed.
            System.Drawing.Image png = System.Drawing.Image.FromFile(@"C:\temp.png");
            //If the image is larger than the default page size, we need to do some more handling.
            if (png.Width >= e.PageBounds.Width && png.Height >= e.PageBounds.Height)
            {
                //Determine if the printout needs to be portrait or landscape, and adjusts accordingly.
                if (png.Width > png.Height)
                    png.RotateFlip(RotateFlipType.Rotate90FlipNone);
                //Draws the image on the output page.
                e.Graphics.DrawImage(png, rect);
            }
            else
                //Otherwise, just draw the image in the top left corner w/out scaling.
                e.Graphics.DrawImageUnscaled(png, 0, 0);
        }

        /// <summary>
        /// Prints C:\temp.png
        /// </summary>
        private static void printPng()
        {
            //Declare a new print document.
            PrintDocument printDoc = new PrintDocument();
            //Use default print settings (including printer name.)
            printDoc.PrinterSettings = new PrinterSettings();
            //Set the event handler for printing a page.
            printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);
            //Start the print.
            printDoc.Print();
        }

        /// <summary>
        /// Pulls a single page out of a multi-page TIFF file.
        /// </summary>
        /// <param name="sourceImage">The multipage source image.</param>
        /// <param name="page">The zero-based page number.</param>
        /// <returns>Reference to the new tiff image.</returns>
        public static System.Drawing.Image getTiffImage(System.Drawing.Image sourceImage, int page)
        {
            //Creates a new MemoryStream for the output image
            MemoryStream ms = new MemoryStream();
            //Creates a new Image for the output image.
            System.Drawing.Image returnImage = null;

            try
            {
                //Get the FrameDimension for the source image.
                FrameDimension objDimension = new FrameDimension(sourceImage.FrameDimensionsList[0]);
                //Select the proper page.
                sourceImage.SelectActiveFrame(objDimension, page);
                //Load the page into the memory stream.
                sourceImage.Save(ms, ImageFormat.Tiff);
                //Generate the new image from the memory stream.
                returnImage = System.Drawing.Image.FromStream(ms);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
                ms.Close();
            }

            return returnImage;
        }
        
        /// <summary>
        /// Initializes the GhostScript process.  Invoke using gs_Run()
        /// </summary>
        public static void gs_Initialize()
        {            
            // Parses information from the registry to determine the version and location of GhostScript
            RegistryKey gsKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\GPL Ghostscript");
            string[] gsKeys = gsKey.GetSubKeyNames();
            gsPath = @"C:\Program Files\gs\gs" + gsKeys[gsKeys.Length - 1] + @"\bin\gswin32.exe";
            //Generates start information for gs based on gsPath.
            gs.StartInfo = new ProcessStartInfo(gsPath);
            //Necessary to allow the execution of gs_Exited.
            gs.EnableRaisingEvents = true;
            gs.Exited += new EventHandler(gs_Exited);
        }

        /// <summary>
        /// Initializes the hpgs process. Invoke using hpgs_Run()
        /// </summary>
        public static void hpgs_Initialize()
        {
            //Sets path to hpgs executable.  Assumes default directory.
            hpgs.StartInfo = new ProcessStartInfo(@"C:\Program Files\hpgs\bin\hpgs.exe");
            //Allow hpgs_Exited to run.
            hpgs.EnableRaisingEvents = true;
            //Set hpgs_Exited to run when process finishes.
            hpgs.Exited += new EventHandler(hpgs_Exited);
        }
        
        /// <summary>
        /// Parses the file extension from an input file.
        /// </summary>
        /// <param name="fileName">File name. Does not need to be fully qualified.</param>
        /// <returns>Extension from input file.</returns>
        public static string fileType(string fileName)
        {
            for (int i = fileName.Length - 1; i >= 0; i--)
            {
                if (fileName[i] == '.')
                    return fileName.Substring(i + 1).ToLower();
            }
            return null;
        }

        /// <summary>
        /// Invokes an instance of gswin32.exe.
        /// </summary>
        /// <param name="args">Command-line arguments for GhostScript.</param>
        private static void gs_Run(string args)
        {
            if (!gsInitialized)
                gs_Initialize();
            gs.StartInfo.Arguments = args;
            gs.Start();
            gs.WaitForExit();
        }

        private static void hpgs_Run(string args)
        {
            if (!hpgsInitialized)
                hpgs_Initialize();
            hpgs.StartInfo.Arguments = args;
            hpgs.Start();
            hpgs.WaitForExit();
        }
        
        /// <summary>
        /// Ensures that gs exited cleanly.  Throws an ApplicationException if it did not.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void gs_Exited(object sender, EventArgs e)
        {
            if (gs.ExitCode != 0)
            {
                MessageBox.Show("GhostScript exited with code " + gs.ExitCode.ToString());
                throw new ApplicationException();
            }
        }

        private static void hpgs_Exited(object sender, EventArgs e)
        {
            if (hpgs.ExitCode != 0)
            {
                MessageBox.Show("HPGS exited with code " + hpgs.ExitCode.ToString());
                throw new ApplicationException();
            }
        }
    }
}
